package com.yyh.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.*;

/**
 * 实体类转Map
 * @author xuan
 * @date 2021/10/27
 */
public class MapModelUtils {

    /**
     * map转为实体类
     * @param map
     * @param model
     * @param <T>
     * @return
     */
    public static <T> T toModel(Map<String, ?> map, T model) {
        T result = model;
        Field[] fields = model.getClass().getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            // 不知道设置值时 需不需设置
            field.setAccessible(true);

            NameInMap annotation = field.getAnnotation(NameInMap.class);
            String key;
            if (null == annotation) {
                key = field.getName();
            } else {
                key = annotation.value();
            }
            Object value = map.get(key);

            if (null != value) {
                // field 为实体类中的类型，value为从map中取出的值
                // map中存在该字段，设置该字段的值
                result = setModelField(result, field, value);
            }
        }
        return result;
    }

    /**
     * 设置对象中字段的值
     * @param model
     * @param field
     * @param value
     * @param <T>
     * @return
     */
    private static <T> T setModelField(T model, Field field, Object value) {
        try {
            Class<?> clazz = field.getType();
            // 字段为值类型时，则转为相应的值类型
            Object resultValue = parseNumber(value, clazz);
            if (MapModel.class.isAssignableFrom(clazz)) {
                // 实现了MapModel接口，即可以在实体类与map之间转化
                Object data = clazz.getConstructor().newInstance();
                if (Map.class.isAssignableFrom(resultValue.getClass())) {
                    // 值为Map类型时
                    field.set(model, toModel((Map) resultValue, data));
                } else {
                    field.set(model, resultValue);
                }
            } else if (Map.class.isAssignableFrom(clazz)) {
                field.set(model, buildObject(resultValue, Map.class, getType(field, 1)));
            } else if (List.class.isAssignableFrom(clazz)) {
                field.set(model, buildObject(resultValue, List.class, getType(field, 1)));
            } else {
                if (field.getName().equals("appPayRequest")) {
                    System.out.println("==========testPayment==========");
                    System.out.println(clazz);
                    System.out.println(confirmType(clazz, value).toString());
                }
                field.set(model, confirmType(clazz, value));
            }

            return model;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage(), e.getCause());
        }
    }

    /**
     * 获取字段的泛型类型，如果字段没有指定泛型类型，则会抛出ClassCastException异常
     * @param field 字段
     * @param index 泛型数组的索引
     * @return
     */
    private static Type getType(Field field, int index) {
        // 必须指定集合的泛型类型
        ParameterizedType genericType = (ParameterizedType) field.getGenericType();
        Type[] actualTypeArguments = genericType.getActualTypeArguments();
        Type type = actualTypeArguments[index];
        return type;
    }

    private static Object buildObject(Object o, Class self, Type subType) {
        Class valueClass = o.getClass();
        Iterator iterator;
        ParameterizedType parameterizedType;
        Type[] types;

        if (Map.class.isAssignableFrom(self) && Map.class.isAssignableFrom(o.getClass())) {
            // 构建Map对象， 传入的也为Map类型对象
            Map<String, Object> valueMap = (Map) o;
            Map<String, Object> result = new HashMap<>();
            iterator = valueMap.entrySet().iterator();

            while (iterator.hasNext()) {
                Map.Entry<String, Object> entry = (Map.Entry) iterator.next();
                if (subType instanceof Class) {
                    // 普通的类型
                    result.put(entry.getKey(), buildObject(entry.getValue(), (Class)subType, null));
                } else {
                    // 参数化类型，泛型类型
                    // Type 与 ParameterizedType
                    // Type的实例 是 Class的子类或实现类 ，ParameterizedType的实例 则不是
                    parameterizedType = (ParameterizedType) subType;
                    types = parameterizedType.getActualTypeArguments();
                    // getRawType() 获得原始类型 。 types[types.length - 1] 获得最后一个参数化类型
                    result.put(entry.getKey(), buildObject(entry.getValue(), (Class)parameterizedType.getRawType(), types[types.length - 1]));
                }
            }

            return result;

        } else if (List.class.isAssignableFrom(self) && List.class.isAssignableFrom(o.getClass())) {
            // 构建List对象， 传入的也为List类型对象
            List valueList = (List) o;
            List result = new ArrayList();
            iterator = valueList.iterator();

            while (iterator.hasNext()) {
                Object element = iterator.next();
                if (subType instanceof Class) {
                    result.add(buildObject(element, (Class)subType, null));
                } else {
                    parameterizedType = (ParameterizedType) subType;
                    types = parameterizedType.getActualTypeArguments();
                    result.add(buildObject(element, (Class) parameterizedType.getOwnerType(), types[types.length-1]));
                }
            }

            return result;

        } else if (MapModel.class.isAssignableFrom(self) && Map.class.isAssignableFrom(o.getClass())) {
            // 构建指定类型对象，需要实现MapModel接口。传入的为Map类型对象
            try {
                return toModel((Map)o, self.newInstance());
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e.getCause());
            }
        } else {
            // 不进行构建
            return o;
        }
    }

    /**
     * 将String类型的值转为数值类型。包括int、long、float、double
     * 否则返回原类型的值
     * 值与类型不匹配时抛出NumberFormatException异常
     * 浮点类型转换时会精度丢失
     * @param value 具体的值
     * @param clazz 字段的类型
     * @return
     */
    private static Object parseNumber(Object value, Class clazz) {
        BigDecimal bigDecimal;
            if (value instanceof String && (clazz == Integer.class || clazz == Integer.TYPE)) {
                bigDecimal = new BigDecimal(value.toString());
                // 如果有小数点，则截断取整。如果为非数值字符，则抛出NumberFormatException异常
                return bigDecimal.intValue();
            } else if (value instanceof String && (clazz == Long.class || clazz == Long.TYPE)) {
                bigDecimal = new BigDecimal(value.toString());
                return bigDecimal.longValue();
            } else if (value instanceof String && (clazz == Float.class && clazz == Float.TYPE)) {
                bigDecimal = new BigDecimal(value.toString());
                // 会精度丢失
                return bigDecimal.floatValue();
            } else if (value instanceof String && (clazz == Double.class && clazz == Double.TYPE)) {
                bigDecimal = new BigDecimal(value.toString());
                // 会精度丢失
                return bigDecimal.doubleValue();
            } else {
                // 返回原始类型的值
                return value;
            }
    }

    /**
     * 转换对象的类型为指定的类型
     * @param expect
     * @param object
     * @return
     */
    private static Object confirmType(Class expect, Object object) {
        if (String.class.isAssignableFrom(expect)) {
            // 预期String类型
            if (object instanceof Number || object instanceof Boolean) {
                return object.toString();
            }
        } else if (Boolean.class.isAssignableFrom(expect)) {
            // 预期Boolean类型
            if (object instanceof String) {
                // 实际String类型
                // 不为true，false之一抛出异常
                return Boolean.valueOf(String.valueOf(object));
            }
            if (object instanceof Integer) {
                // 实际为Integer类型
                if (object.toString().equals("1")) {
                    return true;
                } else if (object.toString().equals("0")) {
                    return false;
                }
            }
        } else if (Integer.class.isAssignableFrom(expect)) {
            // 预期Integer类型
            if (object instanceof String) {
                return Integer.parseInt(object.toString());
            }
            if (object instanceof Long && (Long) object <= 2147483647L) {
                return Integer.parseInt(object.toString());
            }
        } else if (Long.class.isAssignableFrom(expect)) {
            // 期望Long类型
            if (object instanceof String || object instanceof Integer) {
                return Long.parseLong(object.toString());
            }
        } else if (Float.class.isAssignableFrom(expect)) {
            // 期望Float类型
            if (object instanceof String || object instanceof Integer || object instanceof Long) {
                return Float.parseFloat(object.toString());
            }

            if (object instanceof Double && (Double)object <= 3.4028234663852886E38D) {
                return Float.parseFloat(object.toString());
            }
        } else if (Double.class.isAssignableFrom(expect) &&
                (object instanceof String || object instanceof Integer || object instanceof Long || object instanceof Float)) {
            // 期望Double类型
            return Double.parseDouble(object.toString());
        }
        // 返回原类型的值
        return object;
    }


    /**
     * 将实体类转为map，返回一个Map对象。
     * 不包含被排除字段@Exclude标注排除
     *
     * key ==> 默认为字段名，可以通过@NameInMap指定
     * value ==> 为字段的值 转为特定类型
     * @param obj 参数为一个实体类
     * @return 返回一个Map<String, String>对象
     */
    public static Map<String, Object> toMap(Object obj) {
        HashMap<String, Object> map = new HashMap<>();

        try {
            Field[] fields = obj.getClass().getDeclaredFields();
            // 遍历所有字段
            for (Field field : fields) {
                // 防止抛出异常
                field.setAccessible(true);

                ExcludeInMap excludeInMap = field.getAnnotation(ExcludeInMap.class);
                if (null != excludeInMap) {
                    // 排除指定字段
                    continue;
                }

                NameInMap anno = field.getAnnotation(NameInMap.class);
                String key;
                if (anno == null) {
                    // 没有NameInMap 使用字段名
                    key = field.getName();
                } else {
                    // 使用注解指定名称加入map
                    key = anno.value();
                }

                // null != field.get(obj) ==> 排除为空字段
                if (null != field.get(obj) && List.class.isAssignableFrom(field.get(obj).getClass())) {
                    // 字段为List
                    List<Object> arrayField = (List) field.get(obj);
                    List<Object> fieldList = new ArrayList();

                    for (int i = 0; i < arrayField.size(); ++i) {
                        fieldList.add(parseObject(arrayField.get(i)));
                    }

                    map.put(key, fieldList);
                } else if (null != field.get(obj) && MapModel.class.isAssignableFrom(field.get(obj).getClass())) {
                    // 不为list，且实现了MapModel接口
                    map.put(key, toMap(field.get(obj)));
                } else if (null != field.get(obj) && Map.class.isAssignableFrom(field.get(obj).getClass())) {
                    // 对象为map
                    Map<String, Object> valueMap = (Map)field.get(obj);
                    Map<String, Object> result = new HashMap();
                    Iterator var11 = valueMap.entrySet().iterator();

                    while(var11.hasNext()) {
                        Map.Entry<String, Object> entry = (Map.Entry)var11.next();
                        result.put(entry.getKey(), parseObject(entry.getValue()));
                    }

                    map.put(key, result);
                } else if (null != field.get(obj)) {
                    // 普通对象，即没有ToMapAble注解
                    map.put(key, field.get(obj));
                }
            }

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return map;
    }

    /**
     * 继续转换对象
     * @param o
     * @return
     */
    private static Object parseObject(Object o) {
        if (null == o) {
            return o;
        } else {
            Class clazz = o.getClass();
            Iterator listIterator;
            if (List.class.isAssignableFrom(clazz)) {
                // 对象为list
                List<Object> list = (List)o;
                List<Object> result = new ArrayList();
                listIterator = list.iterator();

                while(listIterator.hasNext()) {
                    Object object = listIterator.next();
                    result.add(parseObject(object));
                }

                return result;
            } else if (!Map.class.isAssignableFrom(clazz)) {
                // 对象不为map
                return toMap(o);
            } else {
                // 对象为map
                Map<String, Object> map = (Map)o;
                Map<String, Object> result = new HashMap();
                listIterator = map.entrySet().iterator();

                while(listIterator.hasNext()) {
                    Map.Entry<String, Object> entry = (Map.Entry) listIterator.next();
                    result.put(entry.getKey(), parseObject(entry.getValue()));
                }

                return result;
            }
        }
    }
}
